(function() {

  // Baseline setup
  // --------------

  // 创建根对象, 客户端中的`window` , 服务器中的 `global`.
  var root = this;

  // 保存之前的‘_’常量
  var previousUnderscore = root._;

  // 创建对象保存循环中的中断“break”.
  var breaker = {};

   // 缩短字段:
  var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;

  // 创建短变量，同上.
  var slice            = ArrayProto.slice,
      unshift          = ArrayProto.unshift,
      toString         = ObjProto.toString,
      hasOwnProperty   = ObjProto.hasOwnProperty;

  // 所有**ECMAScript 5** 原生 function 实现重新声明:
  var
    nativeForEach      = ArrayProto.forEach,
    nativeMap          = ArrayProto.map,
    nativeReduce       = ArrayProto.reduce,
    nativeReduceRight  = ArrayProto.reduceRight,
    nativeFilter       = ArrayProto.filter,
    nativeEvery        = ArrayProto.every,
    nativeSome         = ArrayProto.some,
    nativeIndexOf      = ArrayProto.indexOf,
    nativeLastIndexOf  = ArrayProto.lastIndexOf,
    nativeIsArray      = Array.isArray,
    nativeKeys         = Object.keys,
    nativeBind         = FuncProto.bind;

  // 为the Underscore object创建一个安全的调用，待用.
  var _ = function(obj) { return new wrapper(obj); };


  // 向后兼容**Node.js**中的`require()` API，
  // 在客户端通过字符串检测添加一个全局变量`_` ,防止高级压缩出错。
  if (typeof exports !== 'undefined') {
    if (typeof module !== 'undefined' && module.exports) {
      exports = module.exports = _;
    }
    exports._ = _;
  } else {
    root['_'] = _;
  }

  // 当前版本.
  _.VERSION = '1.3.1';

  // Collection Functions
  // --------------------


    // 重要前提，`each`实现声明，又叫做`forEach`，用内建的`forEach` arrays和原始对象处理对象，
	// 假如存在**ECMAScript 5**'s 本地 `forEach`的话，就用现成的。
	var each = _.each = _.forEach = function( obj, iterator, context )
	{
		if( obj == null ) return;

		//Array.prototype.forEach存在，并且obj.forEach === Array.prototype.forEach
		//直接传入
		if( nativeForEach && obj.forEach === nativeForEach )
		{
			obj.forEach(iterator, context);
		}
		
		// +obj.length -->>把字符串装化为数字，这句话的意思是obj是个数组
		else if( obj.length === +obj.length )
		{
			for( var i = 0, l = obj.length; i < l; i++ )
			{
				if( i in obj && iterator.call(context, obj[i], i, obj) === breaker ) return;
			}
		}
		else
		{
			for( var key in obj )
			{
				if( _.has(obj, key) )
				{
					if( iterator.call(context, obj[key], key, obj) === breaker ) return;
				}
			}
		}
	};

	// `map`，返回应用迭代器到每一个元素的结果。返回值 Array
	// 假如存在**ECMAScript 5**'s 本地`map`的话，就用现成的。

	_.map = _.collect = function( obj, iterator, context )
	{
		var results = [];
		if( obj == null ) return results;
		if( nativeMap && obj.map === nativeMap ) return obj.map(iterator, context);
		each(obj, function( value, index, list )
		{
			results[results.length] = iterator.call(context, value, index, list);
		});
		if( obj.length === +obj.length ) results.length = obj.length;
		return results;
	};

	// `reduce`，**Reduce** 从数列返回一个单一值, 又叫做 `inject`或 `foldl`.
	// 假如存在**ECMAScript 5**'s 本地`reduce`的话，就用现成的。
	_.reduce = _.foldl = _.inject = function( obj, iterator, memo, context )
	{
		var initial = arguments.length > 2;
		if( obj == null ) obj = [];
		if( nativeReduce && obj.reduce === nativeReduce )
		{
			if( context ) iterator = _.bind(iterator, context);
			return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
		}
		each(obj, function( value, index, list )
		{
			if( !initial )
			{
				memo = value;
				initial = true;
			}
			else
			{
				memo = iterator.call(context, memo, value, index, list);
			}
		});
		if( !initial ) throw new TypeError('Reduce of empty array with no initial value');
		return memo;
	};

	// `reduceRight`，reduce 的 right-associative 版本, also known as `foldr`.
	// 假如存在**ECMAScript 5**'s 本地`reduceRight`的话，就用现成的。

	_.reduceRight = _.foldr = function( obj, iterator, memo, context )
	{
		var initial = arguments.length > 2;
		if( obj == null ) obj = [];
		if( nativeReduceRight && obj.reduceRight === nativeReduceRight )
		{
			if( context ) iterator = _.bind(iterator, context);
			return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
		}
		var reversed = _.toArray(obj).reverse();
		if( context && !initial ) iterator = _.bind(iterator, context);
		return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);
	};

	// `find`返回第一个true的测试结果  又叫做 `detect`. 返回值 Object
	_.find = _.detect = function( obj, iterator, context )
	{
		var result;
		any(obj, function( value, index, list )
		{
			if( iterator.call(context, value, index, list) )
			{
				result = value;
				return true;
			}
		});
		return result;
	};

	// `filter`，返回真值测试的所有元素。返回值 Array
	// 假如存在**ECMAScript 5**'s 本地`filter` 的话，就用现成的。别名 `select`.
	_.filter = _.select = function( obj, iterator, context )
	{
		var results = [];
		if( obj == null ) return results;
		if( nativeFilter && obj.filter === nativeFilter ) return obj.filter(iterator, context);
		each(obj, function( value, index, list )
		{
			if( iterator.call(context, value, index, list) ) results[results.length] = value;
		});
		return results;
	};

	// 返回真值测试中没有通过的所有元素。返回值 Array
	_.reject = function( obj, iterator, context )
	{
		var results = [];
		if( obj == null ) return results;
		each(obj, function( value, index, list )
		{
			if( !iterator.call(context, value, index, list) ) results[results.length] = value;
		});
		return results;
	};

	// 确定是否所有的元素匹配一个真值测试。返回值  Boolean
	// 假如存在**ECMAScript 5**'s 本地`every`  的话，就用现成的。别名 `all`.
	_.every = _.all = function( obj, iterator, context )
	{
		var result = true;
		if( obj == null ) return result;
		if( nativeEvery && obj.every === nativeEvery ) return obj.every(iterator, context);
		each(obj, function( value, index, list )
		{
			if( !(result = result && iterator.call(context, value, index, list)) ) return breaker;
		});
		return result;
	};

	// `some`，确定是否至少对象中的有一个元素匹配一个真值测试。返回 Boolean
	// 假如存在**ECMAScript 5**'s 本地 `some` 的话，就用现成的。别名 `any`.
	var any = _.some = _.any = function( obj, iterator, context )
	{
		iterator || (iterator = _.identity);
		var result = false;
		if( obj == null ) return result;
		if( nativeSome && obj.some === nativeSome ) return obj.some(iterator, context);
		each(obj, function( value, index, list )
		{
			if( result || (result = iterator.call(context, value, index, list)) ) return breaker;
		});
		return !!result;
	};

	// 确定是否一个给定值包含在在数组中，或者 对象使用`===` 。别名 `contains`

	_.include = _.contains = function( obj, target )
	{
		var found = false;
		if( obj == null ) return found;
		if( nativeIndexOf && obj.indexOf === nativeIndexOf ) return obj.indexOf(target) != -1;
		found = any(obj, function( value )
		{
			return value === target;
		});
		return found;
	};

	// 在参数中集合的每一个成员调用同一个方法
	// _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
	// => [[1, 5, 7], [1, 2, 3]]
	_.invoke = function( obj, method )
	{
		var args = slice.call(arguments, 2);
		return _.map(obj, function( value )
		{
			return (_.isFunction(method) ? method || value : value[method]).apply(value, args);
		});
	};

	// `pluck`是map最常使用的用例模型的版本，即萃取对象数组中某属性值，返回 Array
	_.pluck = function( obj, key )
	{
		return _.map(obj, function( value )
		{
			return value[key];
		});
	};

	// 返回元素最大值或 基于元素计算.
	_.max = function( obj, iterator, context )
	{
		if( !iterator && _.isArray(obj) ) return Math.max.apply(Math, obj);
		if( !iterator && _.isEmpty(obj) ) return -Infinity;
		var result =
		{
			computed: -Infinity
		};
		each(obj, function( value, index, list )
		{
			var computed = iterator ? iterator.call(context, value, index, list) : value;
			computed >= result.computed && (result =
			{
				value: value,
				computed: computed
			});
		});
		return result.value;
	};

	// 返回元素最小值或 基于元素计算.
	_.min = function( obj, iterator, context )
	{
		if( !iterator && _.isArray(obj) ) return Math.min.apply(Math, obj);
		if( !iterator && _.isEmpty(obj) ) return Infinity;
		var result =
		{
			computed: Infinity
		};
		each(obj, function( value, index, list )
		{
			var computed = iterator ? iterator.call(context, value, index, list) : value;
			computed < result.computed && (result =
			{
				value: value,
				computed: computed
			});
		});
		return result.value;
	};

	// 洗牌（乱序）数组。
	_.shuffle = function( obj )
	{
		var shuffled = [], rand;
		each(obj, function( value, index, list )
		{
			if( index == 0 )
			{
				shuffled[0] = value;
			}
			else
			{
				rand = Math.floor(Math.random() * (index + 1));
				shuffled[index] = shuffled[rand];
				shuffled[rand] = value;
			}
		});
		return shuffled;
	};
	// 排序由一个标准迭代器产生的对象的值。
	_.sortBy = function( obj, iterator, context )
	{
		return _.pluck(_.map(obj, function( value, index, list )
		{
			return {
				value: value,
				criteria: iterator.call(context, value, index, list)
			};
		}).sort(function( left, right )
		{
			var a = left.criteria, b = right.criteria;
			return a < b ? -1 : a > b ? 1 : 0;
		}), 'value');
	};

	// 由一个标准值进行组合。
	// 向‘groupby‘传入一个一个字符串属性或一个返回一个标准的函数。

	_.groupBy = function( obj, val )
	{
		var result = {};
		var iterator = _.isFunction(val) ? val : function( obj )
		{
			return obj[val];
		};
		each(obj, function( value, index )
		{
			var key = iterator(value, index);
			(result[key] || (result[key] = [])).push(value);
		});
		return result;
	};

	// 通过比较函数找出一个对象是否被插在保存队列中，使用二进制搜索。
	_.sortedIndex = function( array, obj, iterator )
	{
		iterator || (iterator = _.identity);
		var low = 0, high = array.length;
		while(low < high)
		{
			var mid = (low + high) >> 1;
			iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
		}
		return low;
	};

	// 安全地把迭代器转换成数组
	_.toArray = function( iterable )
	{
		if( !iterable ) return [];
		if( iterable.toArray ) return iterable.toArray();
		if( _.isArray(iterable) ) return slice.call(iterable);
		if( _.isArguments(iterable) ) return slice.call(iterable);
		return _.values(iterable);
	};

	// 返回对象的长度
	_.size = function( obj )
	{
		return _.toArray(obj).length;
	};

	// Array Functions
	// ---------------

	// 返回array（数组）的第一个元素。传递 n参数将返回数组中从第一个元素开始的n个元素。别名 `head`. 
	// 参数 **guard** 通过`_.map`检查数组是否允许工作。
	_.first = _.head = function( array, n, guard )
	{
		return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
	};

	// 返回数组中除了最后一个元素外的其他全部元素。
	// 尤其用于参数对象。传递 n参数将从结果中排除从最后一个开始的n个元素。
	// 参数 **guard** 通过`_.map`检查数组是否允许工作。
	_.initial = function( array, n, guard )
	{
		return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
	};

	// 返回array（数组）的最后一个元素。传递 n参数将返回数组中从最后一个元素开始的n个元素。
	// 参数 **guard** 通过`_.map`检查数组是否允许工作。
	_.last = function( array, n, guard )
	{
		if( (n != null) && !guard )
		{
			return slice.call(array, Math.max(array.length - n, 0));
		}
		else
		{
			return array[array.length - 1];
		}
	};

	// 返回数组中除了第一个元素外的其他全部元素。别名`tail`.
	// 传递 参数**index**将返回数组中从index开始的新数组。
	// 参数 **guard** 通过`_.map`检查数组是否允许工作。
	_.rest = _.tail = function( array, index, guard )
	{
		return slice.call(array, (index == null) || guard ? 1 : index);
	};

	// 删去数组中的所有false值成员。调用 `_.filter`.
	_.compact = function( array )
	{
		return _.filter(array, function( value )
		{
			return !!value;
		});
	};

	// 返回一个单嵌套的array（数组）（嵌套可以是任何深度）。
	_.flatten = function( array, shallow )
	{
		return _.reduce(array, function( memo, value )
		{
			if( _.isArray(value) ) return memo.concat(shallow ? value : _.flatten(value));
			memo[memo.length] = value;
			return memo;
		}, []);
	};

	// 返回一个不存在指定值的数组。
	_.without = function( array )
	{
		return _.difference(array, slice.call(arguments, 1));
	};

	// 生成没有重复的数组，假如数组已经排序的话，你可以选择更快的算法，别名 `unique`.
	_.uniq = _.unique = function( array, isSorted, iterator )
	{
		var initial = iterator ? _.map(array, iterator) : array;
		var result = [];
		_.reduce(initial, function( memo, el, i )
		{
			if( 0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el)) )
			{
				memo[memo.length] = el;
				result[result.length] = array[i];
			}
			return memo;
		}, []);
		return result;
	};

	// 产生一个并集数组，元素来自传入的数组。
	_.union = function()
	{
		return _.uniq(_.flatten(arguments, true));
	};
	// 产生一个交集数组，元素来自传入的数组。 (Aliased as "intersect" for back-compat.)
	_.intersection = _.intersect = function( array )
	{
		var rest = slice.call(arguments, 1);
		return _.filter(_.uniq(array), function( item )
		{
			return _.every(rest, function( other )
			{
				return _.indexOf(other, item) >= 0;
			});
		});
	};

	// 返回并集中第一个数组的私有元素
	_.difference = function( array )
	{
		var rest = _.flatten(slice.call(arguments, 1));
		return _.filter(array, function( value )
		{
			return !_.include(rest, value);
		});
	};

	// 合并多个数列成一个数组，共用索引。
	_.zip = function()
	{
		var args = slice.call(arguments);
		var length = _.max(_.pluck(args, 'length'));
		var results = new Array(length);
		for( var i = 0; i < length; i++ )
			results[i] = _.pluck(args, "" + i);
		return results;
	};

	// 返回value在该 array 中的索引值，如果value不存在 array中就返回-1。
	// 假如存在**ECMAScript 5**'s 本地 `indexOf` 的话，就用现成的。
	// 假如数组是大型的并且已经排序, 参数**isSorted** 传入`true`进行二进制搜索.

	_.indexOf = function( array, item, isSorted )
	{
		if( array == null ) return -1;
		var i, l;
		if( isSorted )
		{
			i = _.sortedIndex(array, item);
			return array[i] === item ? i : -1;
		}
		if( nativeIndexOf && array.indexOf === nativeIndexOf ) return array.indexOf(item);
		for( i = 0, l = array.length; i < l; i++ )
			if( i in array && array[i] === item ) return i;
		return -1;
	};

	// 返回value在该 array 中的从最后开始的索引值，
	// 如果value不存在 array中就返回-1。
	// 假如存在**ECMAScript 5**'s 原生 `lastIndexOf` 的话，就用现成的。
	_.lastIndexOf = function( array, item )
	{
		if( array == null ) return -1;
		if( nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf ) return array.lastIndexOf(item);
		var i = array.length;
		while(i--)
			if( i in array && array[i] === item ) return i;
		return -1;
	};

	// 一个用来创建整数灵活编号的列表的函数，便于each 和 map循环。如果省略start则默认为 0；step 默认为 1.返回一个从start 到stop的整数的列表，用step来增加 （或减少）独占。
	// A port of the native Python `range()` function. 
	// See [the Python documentation](http://docs.python.org/library/functions.html#range).
	_.range = function( start, stop, step )
	{
		if( arguments.length <= 1 )
		{
			stop = start || 0;
			start = 0;
		}
		step = arguments[2] || 1;

		var len = Math.max(Math.ceil((stop - start) / step), 0);
		var idx = 0;
		var range = new Array(len);

		while(idx < len)
		{
			range[idx++] = start;
			start += step;
		}

		return range;
	};

	// Function (ahem) Functions
	// ------------------

	// Reusable constructor function for prototype setting.
	// 为原型设定可重复使用的构造函数
	var ctor = function()
	{
	};

	// 创建一个绑定到一个给定的对象的函数 (指定可选择的 `this`, 和 arguments,
	// ). 绑定带参数又叫做`curry`.

	// 假如存在**ECMAScript 5**'s 原生 `Function.bind` 的话，就用现成的。
	_.bind = function bind( func, context )
	{
		var bound, args;
		if( func.bind === nativeBind && nativeBind ) return nativeBind.apply(func, slice.call(arguments, 1));
		if( !_.isFunction(func) ) throw new TypeError;
		args = slice.call(arguments, 2);
		return bound = function()
		{
			if( !(this instanceof bound) ) return func.apply(context, args.concat(slice.call(arguments)));
			ctor.prototype = func.prototype;
			var self = new ctor;
			var result = func.apply(self, args.concat(slice.call(arguments)));
			if( Object(result) === result ) return result;
			return self;
		};
	};

	// Bind all of an object's methods to that object. Useful for ensuring that
	// all callbacks defined on an object belong to it.
	// 绑定该对象的所有方法到这个对象。确保有用所有定义在该对象的回调属于它。
	_.bindAll = function( obj )
	{
		var funcs = slice.call(arguments, 1);
		if( funcs.length == 0 ) funcs = _.functions(obj);
		each(funcs, function( f )
		{
			obj[f] = _.bind(obj[f], obj);
		});
		return obj;
	};

	// Memoizes方法可以缓存某函数的计算结果
	_.memoize = function( func, hasher )
	{
		var memo = {};
		hasher || (hasher = _.identity);
		return function()
		{
			var key = hasher.apply(this, arguments);
			return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
		};
	};

	// 延迟调用 function ，等待wait（给定）毫秒后调用 ，参数可选 
	_.delay = function( func, wait )
	{
		var args = slice.call(arguments, 2);
		return setTimeout(function()
		{
			return func.apply(func, args);
		}, wait);
	};

	// 延迟调用 function 直到当前调用栈清空。
	_.defer = function( func )
	{
		return _.delay.apply(_,
		[
		    func, 1
		].concat(slice.call(arguments, 1)));
	};

	// Returns a function, that, when invoked, will only be triggered at most once
	// during a given window of time.
	// 返回一个 function ，在调用时，当重复调用函数的时候，最多每隔wait毫秒调用一次该函数。
	_.throttle = function( func, wait )
	{
		var context, args, timeout, throttling, more;
		var whenDone = _.debounce(function()
		{
			more = throttling = false;
		}, wait);
		return function()
		{
			context = this;
			args = arguments;
			var later = function()
			{
				timeout = null;
				if( more ) func.apply(context, args);
				whenDone();
			};
			if( !timeout ) timeout = setTimeout(later, wait);
			if( throttling )
			{
				more = true;
			}
			else
			{
				func.apply(context, args);
			}
			whenDone();
			throttling = true;
		};
	};

	// 返回一个 function 只要它还在被调用，就不会触发，必须在function停止N 毫秒后再被调用。
	_.debounce = function( func, wait )
	{
		var timeout;
		return function()
		{
			var context = this, args = arguments;
			var later = function()
			{
				timeout = null;
				func.apply(context, args);
			};
			clearTimeout(timeout);
			timeout = setTimeout(later, wait);
		};
	};

	// 返回一个 function 最多只能执行一次，不管请求多少次，对延时初始化很有用。
	_.once = function( func )
	{
		var ran = false, memo;
		return function()
		{
			if( ran ) return memo;
			ran = true;
			return memo = func.apply(this, arguments);
		};
	};

	// 返回 第一个 function 作为第二个 wrapper 的参数，
	// 允许你去调用参数，在前后运行代码，有条件地执行原有的 function
	_.wrap = function( func, wrapper )
	{
		return function()
		{
			var args =
			[
				func
			].concat(slice.call(arguments, 0));
			return wrapper.apply(this, args);
		};
	};

	// 返回一个 函数列的嵌套组合 function ，在前的 function 接受在后的返回值
	_.compose = function()
	{
		var funcs = arguments;
		return function()
		{
			var args = arguments;
			for( var i = funcs.length - 1; i >= 0; i-- )
			{
				args =
				[
					funcs[i].apply(this, args)
				];
			}
			return args[0];
		};
	};

	// 返回一个 function ，func 必须在请求N次后才会被执行。
	_.after = function( times, func )
	{
		if( times <= 0 ) return func();
		return function()
		{
			if( --times < 1 )
			{
				return func.apply(this, arguments);
			}
		};
	};

	// Object Functions
	// ----------------

	// 检索object的所有的属性名称。
	// 优先调用 **ECMAScript 5**的原生 `Object.keys`

	_.keys = nativeKeys || function( obj )
	{
		if( obj !== Object(obj) ) throw new TypeError('Invalid object');
		var keys = [];
		for( var key in obj )
			if( _.has(obj, key) ) keys[keys.length] = key;
		return keys;
	};

	// 检索object所有的属性的值。
	_.values = function( obj )
	{
		return _.map(obj, _.identity);
	};

	// 返回一个对象的function names 的排序的列表。
	// Aliased as `methods`
	_.functions = _.methods = function( obj )
	{
		var names = [];
		for( var key in obj )
		{
			if( _.isFunction(obj[key]) ) names.push(key);
		}
		return names.sort();
	};

	// 用传入object(s)的所有属性 扩展给定的 object。
	_.extend = function( obj )
	{
		each(slice.call(arguments, 1), function( source )
		{
			for( var prop in source )
			{
				obj[prop] = source[prop];
			}
		});
		return obj;
	};

	// Fill in a given object with default properties.
	// 用默认值填充给定的 object 
	_.defaults = function( obj )
	{
		each(slice.call(arguments, 1), function( source )
		{
			for( var prop in source )
			{
				if( obj[prop] == null ) obj[prop] = source[prop];
			}
		});
		return obj;
	};

	// 创建一个浅复制的克隆object
	_.clone = function( obj )
	{
		if( !_.isObject(obj) ) return obj;
		return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
	};

	// Invokes interceptor with the obj, and then returns obj.
	// The primary purpose of this method is to "tap into" a method chain, in
	// order to perform operations on intermediate results within the chain.
	//用 obj调用interceptor，然后返回 obj。这种方法的主要目的是"进入"方法链，为了对中间结果链内执行操作。
	_.tap = function( obj, interceptor )
	{
		interceptor(obj);
		return obj;
	};

	// 内部递归比较 function 
	function eq( a, b, stack )
	{
		// Identical objects are equal. `0 === -0`, but they aren't identical.
		// See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
		if( a === b ) return a !== 0 || 1 / a == 1 / b;
		// A strict comparison is necessary because `null == undefined`.
		if( a == null || b == null ) return a === b;
		// Unwrap any wrapped objects.
		if( a._chain ) a = a._wrapped;
		if( b._chain ) b = b._wrapped;
		// Invoke a custom `isEqual` method if one is provided.
		if( a.isEqual && _.isFunction(a.isEqual) ) return a.isEqual(b);
		if( b.isEqual && _.isFunction(b.isEqual) ) return b.isEqual(a);
		// Compare `[[Class]]` names.
		var className = toString.call(a);
		if( className != toString.call(b) ) return false;
		switch( className )
		{
		// Strings, numbers, dates, and booleans are compared by value.
		case '[object String]':
			// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
			// equivalent to `new String("5")`.
			return a == String(b);
		case '[object Number]':
			// `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
			// other numeric values.
			return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
		case '[object Date]':
		case '[object Boolean]':
			// Coerce dates and booleans to numeric primitive values. Dates are compared by their
			// millisecond representations. Note that invalid dates with millisecond representations
			// of `NaN` are not equivalent.
			return +a == +b;
			// RegExps are compared by their source patterns and flags.
		case '[object RegExp]':
			return a.source == b.source && a.global == b.global && a.multiline == b.multiline && a.ignoreCase == b.ignoreCase;
		}
		if( typeof a != 'object' || typeof b != 'object' ) return false;
		// Assume equality for cyclic structures. The algorithm for detecting cyclic
		// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
		var length = stack.length;
		while(length--)
		{
			// Linear search. Performance is inversely proportional to the number of
			// unique nested structures.
			if( stack[length] == a ) return true;
		}
		// Add the first object to the stack of traversed objects.
		stack.push(a);
		var size = 0, result = true;
		// Recursively compare objects and arrays.
		if( className == '[object Array]' )
		{
			// Compare array lengths to determine if a deep comparison is necessary.
			size = a.length;
			result = size == b.length;
			if( result )
			{
				// Deep compare the contents, ignoring non-numeric properties.
				while(size--)
				{
					// Ensure commutative equality for sparse arrays.
					if( !(result = size in a == size in b && eq(a[size], b[size], stack)) ) break;
				}
			}
		}
		else
		{
			// Objects with different constructors are not equivalent.
			if( 'constructor' in a != 'constructor' in b || a.constructor != b.constructor ) return false;
			// Deep compare objects.
			for( var key in a )
			{
				if( _.has(a, key) )
				{
					// Count the expected number of properties.
					size++;
					// Deep compare each member.
					if( !(result = _.has(b, key) && eq(a[key], b[key], stack)) ) break;
				}
			}
			// Ensure that both objects contain the same number of properties.
			if( result )
			{
				for( key in b )
				{
					if( _.has(b, key) && !(size--) ) break;
				}
				result = !size;
			}
		}
		// Remove the first object from the stack of traversed objects.
		stack.pop();
		return result;
	}

	// 对象全等判断
	_.isEqual = function( a, b )
	{
		return eq(a, b, []);
	};

	// 没有可以枚举的私有属性对象，空元素object（array，function，string...）
	_.isEmpty = function( obj )
	{
		if( _.isArray(obj) || _.isString(obj) ) return obj.length === 0;
		for( var key in obj )
			if( _.has(obj, key) ) return false;
		return true;
	};

	// Dom元素判断
	_.isElement = function( obj )
	{
		return !!(obj && obj.nodeType == 1);
	};

	// 数组判断，优先ecma5原生代码
	_.isArray = nativeIsArray || function( obj )
	{
		return toString.call(obj) == '[object Array]';
	};

	// 对象判断
	_.isObject = function( obj )
	{
		return obj === Object(obj);
	};

	// 参数对象判断
	_.isArguments = function( obj )
	{
		return toString.call(obj) == '[object Arguments]';
	};
	if( !_.isArguments(arguments) )
	{
		_.isArguments = function( obj )
		{
			return !!(obj && _.has(obj, 'callee'));
		};
	}

	// 函数对象判断
	_.isFunction = function( obj )
	{
		return toString.call(obj) == '[object Function]';
	};

	// 字符串对象判断
	_.isString = function( obj )
	{
		return toString.call(obj) == '[object String]';
	};

	// 数值对象判断
	_.isNumber = function( obj )
	{
		return toString.call(obj) == '[object Number]';
	};

	// `NaN`数值空值判断 
	_.isNaN = function( obj )
	{
		// `NaN` is the only value for which `===` is not reflexive.
		return obj !== obj;
	};

	// 布尔对象判断
	_.isBoolean = function( obj )
	{
		return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
	};

	// 日期对象判断
	_.isDate = function( obj )
	{
		return toString.call(obj) == '[object Date]';
	};

	// 正则表达式对象判断
	_.isRegExp = function( obj )
	{
		return toString.call(obj) == '[object RegExp]';
	};

	// 空值判断
	_.isNull = function( obj )
	{
		return obj === null;
	};

	// I未定义判断
	_.isUndefined = function( obj )
	{
		return obj === void 0;
	};

	// 私有属性判断
	_.has = function( obj, key )
	{
		return hasOwnProperty.call(obj, key);
	};

	// Utility Functions
	// -----------------

	// Run Underscore.js in *noConflict* mode, returning the `_` variable to its
	// previous owner. Returns a reference to the Underscore object.
	// 重定义`_`，返回 Underscore 对象的引用。
	_.noConflict = function()
	{
		root._ = previousUnderscore;
		return this;
	};

	// Keep the identity function around for default iterators.

	_.identity = function( value )
	{
		return value;
	};

	// 运行 a function **n** 次.
	_.times = function( n, iterator, context )
	{
		for( var i = 0; i < n; i++ )
			iterator.call(context, i);
	};

	// 转义HTML字符串 
	_.escape = function( string )
	{
		return ('' + string).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g, '&#x2F;');
	};

	// 扩展Underscore对象
	_.mixin = function( obj )
	{
		each(_.functions(obj), function( name )
		{
			addToWrapper(name, _[name] = obj[name]);
		});
	};

	// 为需要的客户端模型或DOM元素生成一个全局唯一的id。如果prefix参数存在，id 将附加给它
	var idCounter = 0;
	_.uniqueId = function( prefix )
	{
		var id = idCounter++;
		return prefix ? prefix + id : id;
	};

	// 默认情况, Underscore 使用 ERB-style 模板界定符, 改变以下设置使用可选的界定符.
	_.templateSettings =
	{
		evaluate: /<%([\s\S]+?)%>/g,
		interpolate: /<%=([\s\S]+?)%>/g,
		escape: /<%-([\s\S]+?)%>/g
	};

	// When customizing `templateSettings`, if you don't want to define an
	// interpolation, evaluation or escaping regex, we need one that is
	// guaranteed not to match.

	var noMatch = /.^/;

	// Within an interpolation, evaluation, or escaping, remove HTML escaping
	// that had been previously added.

	var unescape = function( code )
	{
		return code.replace(/\\\\/g, '\\').replace(/\\'/g, "'");
	};

	// JavaScript micro-templating, similar to John Resig's implementation.
	// Underscore templating handles arbitrary delimiters, preserves whitespace,
	// and correctly escapes quotes within interpolated code.

	// 模板编译
	_.template = function( str, data )
	{
		var c = _.templateSettings;
		var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' + 'with(obj||{}){__p.push(\'' + str.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(c.escape || noMatch, function( match, code )
		{
			return "',_.escape(" + unescape(code) + "),'";
		}).replace(c.interpolate || noMatch, function( match, code )
		{
			return "'," + unescape(code) + ",'";
		}).replace(c.evaluate || noMatch, function( match, code )
		{
			return "');" + unescape(code).replace(/[\r\n\t]/g, ' ') + ";__p.push('";
		}).replace(/\r/g, '\\r').replace(/\n/g, '\\n').replace(/\t/g, '\\t') + "');}return __p.join('');";
		var func = new Function('obj', '_', tmpl);
		if( data ) return func(data, _);
		return function( data )
		{
			return func.call(this, data, _);
		};
	};

	// 添加一个 "chain" function, 指派到封装包.
	_.chain = function( obj )
	{
		return _(obj).chain();
	};

	// The OOP Wrapper
	// ---------------

	// If Underscore is called as a function, it returns a wrapped object that
	// can be used OO-style. This wrapper holds altered versions of all the
	// underscore functions. Wrapped objects may be chained.

	var wrapper = function( obj )
	{
		this._wrapped = obj;
	};

	// Expose `wrapper.prototype` as `_.prototype`
	_.prototype = wrapper.prototype;

	// Helper function to continue chaining intermediate results.
	// 链式调用支持
	var result = function( obj, chain )
	{
		return chain ? _(obj).chain() : obj;
	};

	// 方法：轻松地添加 functions 到面向对象的封装。
	var addToWrapper = function( name, func )
	{
		wrapper.prototype[name] = function()
		{
			var args = slice.call(arguments);
			unshift.call(args, this._wrapped);
			return result(func.apply(_, args), this._chain);
		};
	};

	// 添加所有Underscore functions到封装对象。
	_.mixin(_);

	// 添加所有增减数组 functions 到封装包
	each(
	[
	    'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'
	], function( name )
	{
		var method = ArrayProto[name];
		wrapper.prototype[name] = function()
		{
			var wrapped = this._wrapped;
			method.apply(wrapped, arguments);
			var length = wrapped.length;
			if( (name == 'shift' || name == 'splice') && length === 0 ) delete wrapped[0];
			return result(wrapped, this._chain);
		};
	});

	// 添加所有存取数组 functions 到封装包
	each(
	[
	    'concat', 'join', 'slice'
	], function( name )
	{
		var method = ArrayProto[name];
		wrapper.prototype[name] = function()
		{
			return result(method.apply(this._wrapped, arguments), this._chain);
		};
	});

	// 链接封装的 Underscore object.
	wrapper.prototype.chain = function()
	{
		this._chain = true;
		return this;
	};

	// 从已封装的链式对象中提取结果
	wrapper.prototype.value = function()
	{
		return this._wrapped;
	};

}).call(this);
